package com.peaksport.pkms.permit.service;

import java.util.Calendar;
import java.util.Date;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.peaksport.framework.base.dao.IBaseDAO;
import com.peaksport.framework.base.service.CoreBaseService;
import com.peaksport.framework.exception.PKBizException;
import com.peaksport.framework.metadata.filter.FilterInfo;
import com.peaksport.framework.metadata.filter.FilterItemInfo;
import com.peaksport.framework.metadata.view.EntityViewInfo;
import com.peaksport.framework.util.PKPublicUtils;
import com.peaksport.framework.vo.SessionInfo;
import com.peaksport.pkms.oauth.entity.ClientEntity;
import com.peaksport.pkms.oauth.entity.RefreshTokenEntity;
import com.peaksport.pkms.oauth.entity.TokenEntity;
import com.peaksport.pkms.oauth.vo.TokenVO;
import com.peaksport.pkms.permit.entity.UserEntity;

@Service
public class PKOAuthServcie extends CoreBaseService {
	
	@Value("${peak.token.expireTime:172800}") //3600*24*2
	private long token_expireTime;
	@Value("${peak.refreshToken.expireTime:31536000}") //3600*24*365
	private long refresh_token_expireTime;
	
	/**
	 * 注册第三方应用token
	 * @param userNum
	 * @param clientNum
	 * @param clientName
	 * @param clientDesc
	 * @return
	 */
	@Transactional
	public TokenVO register(String userNum,String clientNum,String clientName,String clientDesc) {
		//检查是否已经注册
		StringBuffer sqlCheck = new StringBuffer();
		sqlCheck.append("select 1 from ct_oauth_client where FClientNum='").append(clientNum).append("'");
		boolean b = isExistsForNative(sqlCheck.toString());
		if (b) {
			throw PKBizException.throwException("第三方应用[%s]已经注册.",clientNum);
		}
		//注册
		//1. 创建第三方client端
		StringBuffer sqlUser = new StringBuffer();
		sqlUser.append("select FID,FName_l2,FNumber from t_pm_user where FNumber='").append(userNum).append("'");
		UserEntity userEntity = (UserEntity) queryOneRecordForNative(sqlUser.toString(), UserEntity.class);
		ClientEntity clientEntity = new ClientEntity();
		clientEntity.setUser(userEntity);
		clientEntity.setClientNum(clientNum);
		clientEntity.setClientName(clientName);
		clientEntity.setClientDesc(clientDesc);
		save(clientEntity);
		//2. 生成refreshToken,默认有效期为3个月
		Date now = getCurrentTime();
		RefreshTokenEntity refreshTokenEntity = new RefreshTokenEntity();
		refreshTokenEntity.setClient(clientEntity);
		refreshTokenEntity.setCreateTime(now);
		refreshTokenEntity.setExpireTime(refresh_token_expireTime);
		save(refreshTokenEntity);
		//3. 生成token,默认有效期为2天
		//token,refresh_token
		TokenEntity tokenEntity = new TokenEntity();
		tokenEntity.setClient(clientEntity);
		tokenEntity.setCreateTime(now);
		tokenEntity.setExpireTime(token_expireTime);
		tokenEntity.setRefreshToken(refreshTokenEntity);
		save(tokenEntity);
		
		TokenVO tokenVO = new TokenVO();
		tokenVO.setClientId(clientEntity.getId());
		tokenVO.setClientNum(clientEntity.getClientNum());
		tokenVO.setToken(tokenEntity.getId());
		tokenVO.setToken_createTime(tokenEntity.getCreateTime());
		tokenVO.setToken_expiretime(tokenEntity.getExpireTime());
		tokenVO.setRefresh_token(refreshTokenEntity.getId());
		tokenVO.setRefresh_token_createTime(refreshTokenEntity.getCreateTime());
		tokenVO.setRefresh_token_expireTime(refreshTokenEntity.getExpireTime());
		return tokenVO;
	}
	
	/**
	 * 反注册第三方应用
	 * @param clientId
	 * @return
	 */
	public boolean unRegister(String clientId) {
		String delClient = String.format("delete ct_oauth_client where FID='%s'", clientId);
		String delToken = String.format("delete ct_oauth_token where FClientId='%s'", clientId);
		String delRefreshToken = String.format("delete ct_oauth_refreshtoken where FClientId='%s'", clientId);
		int[] result = batchExecuteSqlForNative(new String[] {delClient,delToken,delRefreshToken});
		return result != null && result.length > 0;
	}
	/**
	 * 重建token
	 * @param userId
	 * @param clientId
	 */
	@Transactional
	public TokenVO rebuildToken(String userId, String clientId) {
		//将现有未过期的token,refresh_token设置过期状态
		String sqlCheck = String.format("select FID,FUserId,FIsEnabled from ct_oauth_client where FID='%s'",clientId);
		ClientEntity clientEntity = (ClientEntity) queryOneRecordForNative(sqlCheck, ClientEntity.class);
		verifyBizNull(clientEntity,"不存在注册的第三方应用[%s]", clientId);
		if (!PKPublicUtils.equals(userId, clientEntity.getUser().getId())) throw PKBizException.throwException("重建token信息必须有原注册人才能重建");
		String updToken = String.format("update ct_oauth_token set FIsExpire=1 where FClientId='%s' and FIsExpire=0", clientId);
		String updRefreshToken =  String.format("update ct_oauth_refreshtoken set FIsExpire=1 where FClientId='%s' and FIsExpire=0", clientId);
		executeSqlForNative(updToken);
		executeSqlForNative(updRefreshToken);
		

		//1. 生成refreshToken,默认有效期为3个月
		Date now = getCurrentTime();
		RefreshTokenEntity refreshTokenEntity = new RefreshTokenEntity();
		refreshTokenEntity.setClient(clientEntity);
		refreshTokenEntity.setCreateTime(now);
		refreshTokenEntity.setExpireTime(refresh_token_expireTime);
		save(refreshTokenEntity);
		//2. 生成token,默认有效期为2天
		//token,refresh_token
		TokenEntity tokenEntity = new TokenEntity();
		tokenEntity.setClient(clientEntity);
		tokenEntity.setCreateTime(now);
		tokenEntity.setExpireTime(token_expireTime);
		tokenEntity.setRefreshToken(refreshTokenEntity);
		save(tokenEntity);
		
		TokenVO tokenVO = new TokenVO();
		tokenVO.setClientId(clientEntity.getId());
		tokenVO.setToken(tokenEntity.getId());
		tokenVO.setToken_createTime(tokenEntity.getCreateTime());
		tokenVO.setToken_expiretime(tokenEntity.getExpireTime());
		tokenVO.setRefresh_token(refreshTokenEntity.getId());
		tokenVO.setRefresh_token_createTime(refreshTokenEntity.getCreateTime());
		tokenVO.setRefresh_token_expireTime(refreshTokenEntity.getExpireTime());
		return tokenVO;
	}
	
	/**
	 * 根据refreshToken重新生产token
	 * @param refreshToken
	 * @param clientId
	 * @return
	 */
	@Transactional
	public TokenVO refreshToken(String refreshToken,String clientId) {
		//校验refresh token
		vaildRefreshToken(refreshToken, clientId);
		String updToken = String.format("update ct_oauth_token set FIsExpire=1 where FClientId='%s' and FIsExpire=0", clientId);
		executeSqlForNative(updToken);
		//2. 生成token,默认有效期为2天
		ClientEntity clientEntity = new ClientEntity();
		clientEntity.setId(clientId);
		RefreshTokenEntity refreshTokenEntity = new RefreshTokenEntity();
		refreshTokenEntity.setId(refreshToken);
		Date now = getCurrentTime();
		TokenEntity tokenEntity = new TokenEntity();
		tokenEntity.setClient(clientEntity);
		tokenEntity.setCreateTime(now);
		tokenEntity.setExpireTime(token_expireTime);
		tokenEntity.setRefreshToken(refreshTokenEntity);
		save(tokenEntity);
		
		TokenVO tokenVO = new TokenVO();
		tokenVO.setClientId(clientEntity.getId());
		tokenVO.setToken(tokenEntity.getId());
		tokenVO.setToken_createTime(tokenEntity.getCreateTime());
		tokenVO.setToken_expiretime(tokenEntity.getExpireTime());
		return tokenVO;
	}
	
	/**
	 * 校验refresh token
	 * @param refreshToken
	 * @param clientId
	 */
	private void vaildRefreshToken(String refreshToken,String clientId) {
		String sql = String.format("select FClientId,FCreateTime,FExpireTime,FISExpire from ct_oauth_refreshtoken where FID='%s'",refreshToken);
		RefreshTokenEntity refreshTokenEntity = (RefreshTokenEntity) queryOneRecordForNative(sql, RefreshTokenEntity.class);
		verifyBizNull(refreshTokenEntity, "不存在refreshToken[%s]",refreshToken);
		boolean isExpire = refreshTokenEntity.isExpire();
		if (isExpire) throw PKBizException.throwException("refreshToken[%s]已经过期,请重建token", refreshToken);
		Date createTime = refreshTokenEntity.getCreateTime();
		long expireTime = refreshTokenEntity.getExpireTime();
		Date now = getCurrentTime();
		Calendar dateExpireTime = Calendar.getInstance();
		dateExpireTime.setTime(createTime);
		dateExpireTime.add(Calendar.SECOND, (int)expireTime);
		if (now.compareTo(dateExpireTime.getTime()) > 0)
			throw PKBizException.throwException("refreshToken[%s]已经过期,请重建token", refreshToken);
	}
	
	/**
	 * 验证token
	 * @param token
	 */
	public TokenVO vaildToken(String token) {
		String sqlToken =String.format("select FClientId,FCreateTime,FExpireTime,FISExpire from ct_oauth_token where FID='%s'",token);
		TokenEntity tokenEntity = (TokenEntity) queryOneRecordForNative(sqlToken.toString(), TokenEntity.class);
		verifyBizNull(tokenEntity, "不存在token[%s]",token);
		boolean isExpire = tokenEntity.isExpire();
		if (isExpire) throw PKBizException.throwException("token[%s]已经过期,请获取新的token", token);
		Date createTime = tokenEntity.getCreateTime();
		long expireTime = tokenEntity.getExpireTime();
		Date now = getCurrentTime();
		Calendar dateExpireTime = Calendar.getInstance();
		dateExpireTime.setTime(createTime);
		dateExpireTime.add(Calendar.SECOND, (int)expireTime);
		if (now.compareTo(dateExpireTime.getTime()) > 0)
			throw PKBizException.throwException("token[%s]已经过期,请获取新的token", token);
		String sqlClient = String.format("select FUserId,FIsEnabled from ct_oauth_client where FID='%s'", tokenEntity.getClient().getId());
		ClientEntity clientEntity = (ClientEntity)queryOneRecordForNative(sqlClient.toString(),ClientEntity.class);
		verifyBizNull(clientEntity, "token[%s]不存在对应的客户端,请联系系统管理员.",token);
		TokenVO tokenVO = new TokenVO();
		tokenVO.setClientId(tokenEntity.getClient().getId());
		tokenVO.setUserId(clientEntity.getUser().getId());
		return tokenVO;
	}
	
	/**
	 * 自动更新token有效期
	 */
	public void autoExpireToken() {
		String updToken = "update ct_oauth_token set FIsExpire=1 where sysdate>fcreatetime+fexpiretime/24/3600 and FIsExpire=0";
		String updRefreshToken = "update ct_oauth_RefreshToken set FIsExpire=1 where sysdate>fcreatetime+fexpiretime/24/3600 and FIsExpire=0";
		batchExecuteSqlForNative(new String[] {updToken,updRefreshToken});
	}
	
	/**
	 * //TODO 目前简单处理,根据url匹配方式，后续看具体权限配置，使用正则表达式的方法
	 * 校验客户端是否有调用接口的权限
	 * @param clientId
	 * @param url
	 * @return
	 */
	
	public boolean vaildPermit(String clientId, String url) {
		verifyBizNull(clientId, "clientId不能为空");
		verifyBizNull(url,"url不能为空");
		String sql = String.format("select 1 from ct_oauth_permit p where FClientId='%s' and '%s' like FUrl", clientId,url) + " || '%'";
		boolean b = isExistsForNative(sql);
		if (!b) {
			String sqlClient = "select decode(FClientName,null,FClientNum,FClientName) from ct_oauth_client where FID='" + clientId + "'";
			String clientNum = queryOneValueForNative(sqlClient,String.class);
			throw PKBizException.throwException("第三方客户端[%s]没有访问[%s]的权限.",clientNum,url);
		}
		return b;
	}
	
	/**
	 * 批量删除过期的token、refresh token
	 * @param clientId 当clientId为空时,删除所有第三方client的过期token、refresh token
	 */
	@Transactional
	public void removeExpireAllToken(String clientId) {
		String delExpireToken = "delete ct_oauth_token where FIsExpire=1";
		String delExpireRefreshToken = "delete ct_oauth_refreshtoken where FIsExpire=1";;
		if (!PKPublicUtils.isEmpty(clientId)) {
			delExpireToken = delExpireToken + "and FClientId='" + clientId + "'";
			delExpireRefreshToken = delExpireRefreshToken + "and FClientId='" + clientId + "'";
		}
		batchExecuteSqlForNative(delExpireToken,delExpireRefreshToken);
		
	}
	
	/**
	 * 根据token获取contextInfo
	 * @param token
	 */
	
	public SessionInfo getContextByToken(String token) {
		
		StringBuffer sqlTokenClient = new StringBuffer();
		sqlTokenClient.append("select FID,FUserId,FClientNum,FClientName,FClientDesc ")
		.append("from ct_oauth_client ")
		.append("where FID=(select FClientID from ct_oauth_token where FID='").append(token).append("')");
		ClientEntity clientEntity = queryOneRecordForNative(sqlTokenClient.toString(), ClientEntity.class);
		
		EntityViewInfo entityUser = new EntityViewInfo();
		FilterInfo filterInfo = new FilterInfo();
		filterInfo.addFilterItemInfo(new FilterItemInfo("id",clientEntity.getUser().getId()));
		entityUser.setFilterInfo(filterInfo);
		UserEntity userEntity = getValue(entityUser, UserEntity.class);
		String sessionId = token;
		SessionInfo sessionInfo = new SessionInfo();
		sessionInfo.setTokenLogin(true);
		sessionInfo.setSessionId(sessionId);
		sessionInfo.setToken(token);
		sessionInfo.setClientEntity(clientEntity);
		sessionInfo.setUserEntity(userEntity);
		setSessionInfo(sessionInfo);
		return sessionInfo;
		
	}	
	
	@Override
	public IBaseDAO<?> getDAO() {
		return null;
	}

	
}
